﻿using DotNetCommon.Data;
using DotNetCommon.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DotNetCommon;

/// <summary>
/// Roslyn编译器帮助类
/// </summary>
public class RoslynHelper
{
    static string assemblyName = DateTime.Now.ToFileNameGuidString(".dll");

    static HashSet<Assembly> GetSolutionAssemblies()
    {
        var assems = AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic && x.Location.IsNotNullOrWhiteSpace());
        var assemblies = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
                            .Select(x => Assembly.Load(AssemblyName.GetAssemblyName(x)));
        HashSet<Assembly> hashSet = new HashSet<Assembly>();
        foreach (var assembly in assemblies)
        {
            hashSet.Add(assembly);
        }
        foreach (var assm in assems)
        {
            hashSet.Add(assm);
        }
        return hashSet;
    }
    static IEnumerable<MetadataReference> references = GetSolutionAssemblies()
            .Where(x => !x.IsDynamic && x.Location.IsNotNullOrWhiteSpace())
            .Select(x => MetadataReference.CreateFromFile(x.Location));
    static CSharpCompilation compilation = CSharpCompilation.Create(assemblyName, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

    /// <summary>
    /// 动态编译到内存,并加载
    /// </summary>
    /// <param name="code">需要动态编译的代码</param>
    /// <returns>动态生成的程序集</returns>
    public static Result<Assembly> GenerateAssemblyFromCode(string code)
    {
        Assembly assembly = null;
        string msg = "";
        AsyncLocker.LockAsync(typeof(RoslynHelper).GetClassFullName(), () =>
        {
            // 丛代码中转换表达式树
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);

            // 创建编译对象
            compilation = compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(syntaxTree);

            using (var ms = new MemoryStream())
            {
                // 将编译好的IL代码放入内存流
                EmitResult result = compilation.Emit(ms);

                // 编译失败，提示
                if (!result.Success)
                {
                    var sb = new StringBuilder();
                    IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                                diagnostic.IsWarningAsError ||
                                diagnostic.Severity == DiagnosticSeverity.Error);
                    foreach (Diagnostic diagnostic in failures)
                    {
                        sb.AppendLine($"{diagnostic.Id}: {diagnostic.GetMessage()}");
                    }
                    msg = sb.ToString();
                }
                else
                {
                    // 编译成功，从内存中加载编译好的程序集
                    ms.Seek(0, SeekOrigin.Begin);
                    var arr = ms.ToArray();
                    assembly = Assembly.Load(arr);
                }
            }
            return Task.CompletedTask;
        }).Wait();
        if (assembly != null) return Result.Ok(assembly);
        return Result.NotOk(msg);
    }
}
